
package com.trydofor.id.core;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.annotation.Nullable;

import com.trydofor.aloe.exception.FakeException;

/**
 * A Common Sequence/Queue. (a9 seek you :-D)
 * 
 * @param <T> the Type of sequence/queue
 * @author Shi Rongjiu (www.trydofor.com)
 * @since 0.9
 */
public interface A9CQ<T> {
    
    /**
     * unknown remain
     */
    long REMAIN_UNKNOWN = Long.MIN_VALUE;
    
    /**
     * whether the sequence is atomic (means thread-safe).
     * default {@code true}.
     * 
     * @return {@code true} if atomic.
     */
    boolean isAtomic();
    
    /**
     * get the remaining(means unused) of sequence.
     * REMAIN_UNKNOWN if unknown
     * 
     * @see #REMAIN_UNKNOWN
     * @return the remaining
     */
    long remains();
    
    /**
     * reset the follow state.
     * 
     * <pre>
     *  - remains() - calculate again.
     *  - current() - {@code null}. 
     *  - next() - before the first invocation.
     *  - next(long, TimeUnit) - before the first invocation.
     * </pre>
     */
    void reset();
    
    /**
     * get the current value.
     * 
     * @return @Nullable the current value, {@code null} if no current.
     */
    @Nullable
    T current();
    
    /**
     * get the next value, waiting if necessary until completed.
     * 
     * @return @Nullable the next sequence, {@code null} if no value.
     * @throws FakeException if got an exception
     */
    @Nullable
    T next();
    
    /**
     * get the next value, waiting if necessary for at most the given time.
     * if the timeout is not positive or the unit is null, it acts as next();
     * 
     * @param timeout the maximum time to wait
     * @param unit @Nullable the time unit of the timeout argument
     * @return @Nullable the next sequence, {@code null} if no value.
     * @throws InterruptedException if the current thread was interrupted while waiting
     * @throws TimeoutException if the wait timed out
     * @throws FakeException if got a handled exception
     */
    @Nullable
    T next(long timeout, @Nullable TimeUnit unit) throws InterruptedException, TimeoutException;
}
